/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.client;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * Contains the reason why a command failed.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 16 December 2010
 */
public final class OPCommandFailureReason extends JavaScriptObject {

    // different types of failure. Not put in an enum because it is put in a field of the javascript object and should be transfered between module.
    /**
     * Failure : the command is unknown
     */
    public static final int TYPE_UNKNOW_COMMAND = 0;

    /**
     * Failure for loadPart command: the loading of the part was timed out. Params contains the name of the service.
     */
    public static final int TYPE_LOAD_TIMEOUT = 1;

    /**
     * Failure for display view command: the view to display is not binded. Params contains the name of the service, of the resource, and of the action.
     */
    public static final int TYPE_NO_SUCH_VIEW = 2;

    /**
     * Failure for display view: there is no slot with the given slot id on the page to attach a view on it.
     */
    public static final int TYPE_NO_SUCH_SLOT = 3;

    /**
     * Failure for display view command: the view needs mandatory params and those are not given in the call. Params contains the name of the missing param.
     */
    public static final int TYPE_MISSING_MANDATORY_PARAMS = 4;

    /**
     * Failure for dispatchEventTo command: the specified service is not binded. Params contains the name of the service.
     */
    public static final int TYPE_NO_SUCH_SERVICE = 5;

    /**
     * Failure for display view and dispatch event to commands: the pre loading command failed. Params contains the name of the service that failed loading.
     */
    public static final int TYPE_PRE_COMMAND_FAILED = 6;

    /**
     * Failure for dispatchMessageToEmbeddedView and dispatchMessageToContainerSlot command: there is no element in the page that have the given id.
     */
    public static final int TYPE_NO_SUCH_ID_IN_THE_PAGE = 7;

    /**
     * Failure for dispatchMessageToEmbeddedView and dispatchMessageToContainerSlot command: the given id is not attached to an {@link OPSlot} slot.
     */
    public static final int TYPE_GIVEN_ID_IS_NOT_A_SLOT = 8;

    /**
     * Failure for dispatchMessageToEmbeddedView command: there is no view attached to this proxy.
     */
    public static final int TYPE_NO_VIEW_ATTACHED_TO_PROXY = 9;

    /**
     * Failure for dispatchMessageToContainerSlot command: there is no slot attached to this proxy.
     */
    public static final int TYPE_NO_SLOT_ATTACHED_TO_PROXY = 10;

    // names used in params
    /**
     * Name used in params to save the command type.
     */
    public static final String PARAM_COMMAND_TYPE = "command_type";

    /**
     * Name used in params to save the service name.
     */
    public static final String PARAM_SERVICE = "service";

    /**
     * Name used in params to save the resource name.
     */
    public static final String PARAM_RESOURCE = "resource";

    /**
     * Name used in params to save the action name.
     */
    public static final String PARAM_ACTION = "action";

    /**
     * Name used in params to save the event.
     */
    public static final String PARAM_MISSING_PARAM = "missing_param";

    /**
     * Name used in params to save the slot id.
     */
    public static final String PARAM_SLOT_ID = "slotId";

    // human readable failure type description

    /**
     * The command is unknown
     */
    protected static final String DESCRIPTION_TYPE_UNKNOW_COMMAND = "Unknown command";

    /**
     * The loading of the part was timed out
     */
    protected static final String DESCRIPTION_TYPE_LOAD_TIMEOUT = "Load timeout";

    /**
     * The view to display is not binded
     */
    protected static final String DESCRIPTION_TYPE_NO_SUCH_VIEW = "No such view";

    /**
     * There is no slot with the given slot id on the page
     */
    protected static final String DESCRIPTION_TYPE_NO_SUCH_SLOT = "No such slot";

    /**
     * The view needs mandatory params and those are not given in the call
     */
    protected static final String DESCRIPTION_TYPE_MISSING_MANDATORY_PARAMS = "Missing mandatory param";

    /**
     * The specified service is not binded
     */
    protected static final String DESCRIPTION_TYPE_NO_SUCH_SERVICE = "No such service";

    /**
     * The pre loading command failed
     */
    protected static final String DESCRIPTION_TYPE_PRE_COMMAND_FAILED = "Pre-command failed";

    /**
     * There is no element on the page that have the given id
     */
    protected static final String DESCRIPTION_TYPE_NO_SUCH_ID_IN_THE_PAGE = "No such id in the page";

    /**
     * The given id is not attached to an OPSlot slot..
     */
    protected static final String DESCRIPTION_TYPE_GIVEN_ID_IS_NOT_A_SLOT = "Given id is not a slot";

    /**
     * There is no view attached to this proxy.
     */
    protected static final String DESCRIPTION_TYPE_NO_VIEW_ATTACHED_TO_PROXY = "No view attached to proxy";

    /**
     * There is no slot attached to this proxy.
     */
    protected static final String DESCRIPTION_TYPE_NO_SLOT_ATTACHED_TO_PROXY = "No slot attached to proxy";

    /**
     * Return a commandFailureReason.
     * 
     * @param type
     *            the type of the failure.
     * @param params
     *            parameters containing data about the failure, if needed. Can be null
     * @return the command failure reason
     */
    public static final OPCommandFailureReason commandFailureReasonFactory(final int type, final OPParams params) {
        OPParamsJS paramsJS = null;
        if (params != null) {
            paramsJS = OPParamsJS.buildFromOPParams(params);
        }

        return commandFailureReasonFactoryImpl(type, paramsJS);
    }

    /**
     * Real implementation of commandFailureReason.
     * 
     * @param type
     *            the type of the failure.
     * @param params
     *            parameters containing data about the failure, if needed. Can be null
     * @return the command failure reason
     */
    private static final native OPCommandFailureReason commandFailureReasonFactoryImpl(final int type, final OPParamsJS params) /*-{
        var failureReason = {};
        failureReason.type = type;
        failureReason.params = params;
        return failureReason;
    }-*/;

    /**
     * Returns a command failure reason for an unknown command type.
     * 
     * @param commandType
     *            the unknown command type.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason unknownCommandFailureFactory(final int commandType) {
        return commandFailureReasonFactory(TYPE_UNKNOW_COMMAND, new OPParams(PARAM_COMMAND_TYPE, String.valueOf(commandType)));
    }

    /**
     * Returns a command failure reason for a load timeout failure.
     * 
     * @param service
     *            the service that failed to load. Shouldn't be null.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason loadTimeoutFailureFactory(final String service) {
        assert service != null : "parameter service shouldn't be null";

        return commandFailureReasonFactory(TYPE_LOAD_TIMEOUT, new OPParams(PARAM_SERVICE, service));
    }

    /**
     * Returns a command failure reason for a no such view failure.
     * 
     * @param service
     *            the service for the view. Shouldn't be null.
     * @param resource
     *            the resource for the view. Shouldn't be null.
     * @param action
     *            the action for the view. Shoudn't be null.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason noSuchViewFailureFactory(final String service, final String resource, final String action) {
        assert service != null : "parameter service shouldn't be null";
        assert resource != null : "parameter resource shouldn't be null";
        assert action != null : "parameter action shouldn't be null";

        return commandFailureReasonFactory(TYPE_NO_SUCH_VIEW, new OPParams(PARAM_SERVICE, service, PARAM_RESOURCE, resource, PARAM_ACTION, action));
    }

    /**
     * Returns a command failure reason for a no such slot failure.
     * 
     * @param slotId
     *            the id of the missing slot. Shouldn't be null, shouldn't be empty.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason noSuchSlotFailureFactory(final String slotId) {
        assert slotId != null : "parameter slotId shouldn't be null";
        assert !slotId.isEmpty() : "parameter slotId shouldn't be empty";

        return commandFailureReasonFactory(TYPE_NO_SUCH_SLOT, new OPParams(PARAM_SLOT_ID, slotId));
    }

    /**
     * Returns a command failure reason for a missing mandatory param failure.
     * 
     * @param missingParam
     *            the name of the missing mandatory param. Shouldn't be null.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason missingMandatoryParamFailureFactory(final String missingParam) {
        assert missingParam != null : "parameter missingParam shouldn't be null";

        return commandFailureReasonFactory(TYPE_MISSING_MANDATORY_PARAMS, new OPParams(PARAM_MISSING_PARAM, missingParam));
    }

    /**
     * Returns a command failure reason for a no such service failure.
     * 
     * @param service
     *            the service missing. Shouldn't be null.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason noSuchServiceFailureFactory(final String service) {
        assert service != null : "parameter service shouldn't be null";

        return commandFailureReasonFactory(TYPE_NO_SUCH_SERVICE, new OPParams(PARAM_SERVICE, service));
    }

    /**
     * Returns a command failure reason for a pre command failed failure.
     * 
     * @param service
     *            the service that failed loading in the pre command. Shouldn't be null.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason preCommandFailedFailureFactory(final String service) {
        assert service != null : "parameter service shouldn't be null";

        return commandFailureReasonFactory(TYPE_PRE_COMMAND_FAILED, new OPParams(PARAM_SERVICE, service));
    }

    /**
     * Returns a command failure reason for a no such id on the page failure.
     * 
     * @param slotId
     *            the id that was searched on the page. Shouldn't be null, shouldn't be empty.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason noSuchIdOnThePageFailureFactory(final String slotId) {
        assert slotId != null : "parameter slotId shouldn't be null";
        assert !slotId.isEmpty() : "parameter slotId shouldn't be empty";

        return commandFailureReasonFactory(TYPE_NO_SUCH_ID_IN_THE_PAGE, new OPParams(PARAM_SLOT_ID, slotId));
    }

    /**
     * Returns a command failure reason for a given id is not a slot failure.
     * 
     * @param slotId
     *            the id that is not attached to a OPSlot. Shouldn't be null, shouldn't be empty.
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason givenIdIsNotASlotFailureFactory(final String slotId) {
        assert slotId != null : "parameter slotId shouldn't be null";
        assert !slotId.isEmpty() : "parameter slotId shouldn't be empty";

        return commandFailureReasonFactory(TYPE_GIVEN_ID_IS_NOT_A_SLOT, new OPParams(PARAM_SLOT_ID, slotId));
    }

    /**
     * Returns a command failure reason for a no view attached to proxy failure.
     * 
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason noViewAttachedToProxyFailureFactory() {
        return commandFailureReasonFactory(TYPE_NO_VIEW_ATTACHED_TO_PROXY, null);
    }

    /**
     * Returns a command failure reason for a no slot attached to proxy failure.
     * 
     * @return the command failure reason.
     */
    public static final OPCommandFailureReason noSlotAttachedToProxyFailureFactory() {
        return commandFailureReasonFactory(TYPE_NO_SLOT_ATTACHED_TO_PROXY, null);
    }

    /**
     * Protected constructor, necessary because it's a JavaScriptObject.
     */
    protected OPCommandFailureReason() {}

    /**
     * Returns the type of the command failure reason.
     * 
     * @return the type of the command.
     */
    public final native int getType() /*-{
        return this.type;
    }-*/;

    /**
     * Returns the parameters of the command failure reason.
     * 
     * @return the parameters. Can be null.
     */
    public final OPParams getParams() {
        final OPParamsJS paramsJS = getParamsImpl();
        OPParams params = null;
        if (paramsJS != null) {
            params = paramsJS.toOPParams();
        }
        return params;
    }

    /**
     * Real implementation of returning the parameters of the command failure reason.
     * 
     * @return the parameters. Can be null.
     */
    public final native OPParamsJS getParamsImpl() /*-{
        return this.params;
    }-*/;

    /**
     * Returns a human readable description of the failure reason type. Useful for debugging. Unfortunately, cannot override toString as it is final in
     * JavaScriptObject.
     * 
     * @return a human-readable description of the failure reason type, useful for debugging.
     */
    public String getTypeDescription() {
        return getTypeDescription(getType());
    }

    /**
     * Returns a human readable description of the given failure reason type. Useful for debugging.
     * 
     * @param type
     *            the type for which to return a description
     * @return a human-readable description of the failure reason type, useful for debugging.
     */
    public static String getTypeDescription(final int type) {
        switch (type) {
        case TYPE_UNKNOW_COMMAND:
            return DESCRIPTION_TYPE_UNKNOW_COMMAND;
        case TYPE_LOAD_TIMEOUT:
            return DESCRIPTION_TYPE_LOAD_TIMEOUT;
        case TYPE_NO_SUCH_VIEW:
            return DESCRIPTION_TYPE_NO_SUCH_VIEW;
        case TYPE_NO_SUCH_SLOT:
            return DESCRIPTION_TYPE_NO_SUCH_SLOT;
        case TYPE_MISSING_MANDATORY_PARAMS:
            return DESCRIPTION_TYPE_MISSING_MANDATORY_PARAMS;
        case TYPE_NO_SUCH_SERVICE:
            return DESCRIPTION_TYPE_NO_SUCH_SERVICE;
        case TYPE_PRE_COMMAND_FAILED:
            return DESCRIPTION_TYPE_PRE_COMMAND_FAILED;
        case TYPE_NO_SUCH_ID_IN_THE_PAGE:
            return DESCRIPTION_TYPE_NO_SUCH_ID_IN_THE_PAGE;
       case TYPE_GIVEN_ID_IS_NOT_A_SLOT:
            return DESCRIPTION_TYPE_GIVEN_ID_IS_NOT_A_SLOT;
        case TYPE_NO_VIEW_ATTACHED_TO_PROXY:
            return DESCRIPTION_TYPE_NO_VIEW_ATTACHED_TO_PROXY;
       case TYPE_NO_SLOT_ATTACHED_TO_PROXY:
           return DESCRIPTION_TYPE_NO_SLOT_ATTACHED_TO_PROXY;
       default:
           return "Unknown failure reason type";
        }
    }

    /**
     * Returns a human readable description of the failure reason. Useful for debugging. Unfortunately, cannot override toString as it is final in
     * JavaScriptObject.
     * 
     * @return a human-readable description of the failure reason, useful for debugging.
     */
    public String getDescription() {
        String description = getTypeDescription() + ": ";

        switch (getType()) {
        case TYPE_UNKNOW_COMMAND:
            description += "Command type " + getParams().getParamValue(PARAM_COMMAND_TYPE) + " is unknown.";
            break;
        case TYPE_LOAD_TIMEOUT:
            description += "Loading service " + getParams().getParamValue(PARAM_SERVICE) + " failed (timed out).";
            break;
        case TYPE_NO_SUCH_VIEW:
            description += "Cannot find a view binded for service " + getParams().getParamValue(PARAM_SERVICE) + ", resource "
                    + getParams().getParamValue(PARAM_RESOURCE) + " and action " + getParams().getParamValue(PARAM_ACTION) + ".";
            break;
        case TYPE_NO_SUCH_SLOT:
            description += "Cannot find a slot with the id " + getParams().getParamValue(PARAM_SLOT_ID) + " in the page.";
            break;
        case TYPE_MISSING_MANDATORY_PARAMS:
            description += "Calling view display failed because the mandatory param " + getParams().getParamValue(PARAM_MISSING_PARAM) + " was missing.";
            break;
        case TYPE_NO_SUCH_SERVICE:
            description += "Cannot find a binded service named " + getParams().getParamValue(PARAM_SERVICE) + ".";
            break;
        case TYPE_PRE_COMMAND_FAILED:
            description += "The service " + getParams().getParamValue(PARAM_SERVICE)
                    + ", necessary for the command, wasn't loaded. Trying to load it, before executing the command, failed (timeout).";
            break;
        case TYPE_NO_SUCH_ID_IN_THE_PAGE:
            description += "Cannot find an element with the id " + getParams().getParamValue(PARAM_SLOT_ID) + " in the page.";
            break;
        case TYPE_GIVEN_ID_IS_NOT_A_SLOT:
            description += "The element with the id " + getParams().getParamValue(PARAM_SLOT_ID) + " found in the page is not a slot.";
            break;
        case TYPE_NO_VIEW_ATTACHED_TO_PROXY:
            description += "There is no view attached to this proxy.";
            break;
        case TYPE_NO_SLOT_ATTACHED_TO_PROXY:
            description += "There is no slot attached to this proxy.";
            break;
        default:
            description += "Executing the command failed for an unknown reason.";
            break;
        }

        return description;
    }
}
